# -*- coding: utf-8 -*-
# @Author  : Chinesejun
# @Email   : itcast@163.com
# @File    : 01-pytorch的基本语法.py
# @Software: PyCharm


'''
在开头加上from __future__ import print_function这句之后，即使在python2.X，使用print就得像python3.X那样加括号使用。
python2.X中print不需要括号，而在python3.X中则需要
'''
# print xxx  --python2
# print(xxxx) --python3
from __future__ import print_function
import torch

# # 创建矩阵的操作
# x = torch.empty(5, 3)
# print(x)
#
# # 创建一个有初始化的矩阵
x = torch.rand(5, 3)
print(x)

# 创建一个全零矩阵并可指定数据元素的类型为long
# x = torch.zeros(5, 3, dtype=torch.long)
# print(x)
# print(x.type()) # 打印数据类型
# print(type(x)) # 打印整体类型

# 直接通过数据创建张量
# x = torch.tensor([2.5, 3.5])
# print(x)

# 通过已有的一个张量创建相同尺寸的新张量
# x = x.new_ones(5, 3, dtype=torch.double)
# print(x.type())
# x = x.new_ones(5, 3, dtype=torch.long)
# print(x)
# #
# y = torch.randn_like(x, dtype=torch.float)
# print(y)
# #
# # #得到张量的尺寸
# # print(x.size())
#
# # 加法操作
# y = torch.rand(5, 3)
# # print(x+y)
# #
# # # 函数加法
# # print(torch.add(x, y))
#
# # 加法操作3
# result = torch.empty(5, 3)
# torch.add(x, y, out=result)
# # 等效的
# a = torch.add(x, y)
# print(a)
# print(result)

# 第四种加法方式: in-place (原地置换)
# y.add_(x)
# print(y)

# 用类似于Numpy的方式对张量进行操作

print('截断====', x[:, 1])

# 改变张量的形状: torch.view()
# x = torch.randn(4, 4)
# y = x.view(16)
# z = x.view(-1, 8)
# print(x.size(), y.size(), z.size())

# 如果张量中只有一个元素, 可以用.item()将值取出, 作为一个python number
# x = torch.randn(1)
# print(x)
# print(x.item())


# a = torch.ones(5)
# print(a)

# Torch Tensor转化为Numpy array
# b = a.numpy()
# print(b)

# 验证Torch Tensor和Numpy array共享底层的内存空间
# a.add_(1)
# print(a)
# print(b)

# 将Numpy array转换为Torch Tensor
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
# b = torch.tensor(a) # 这个是不共享内存的
np.add(a, 1, out=a)
print(a)
print(b)

# 如果服务器上已经安装了GPU和CUDA

if torch.cuda.is_available():
    # 定义一个设备对象, 这里指定成CUDA, 即使用GPU
    device = torch.device('cuda')
    # 直接在GPU上创建一个Tensor
    y = torch.ones_like(x, device=device)
    # 将在CPU上面的x张量移动到GPU上面
    x = x.to(device)
    # x和y都在GPU上面, 才能支持加法运算
    z = x + y
    # 此处的张量z在GPU上面
    print(z)
    # 也可以将z转移到CPU上面, 并同时指定张量元素的数据类型
    print(z.to('cpu', torch.double))
print('执行完成====')

# =====pytorch中的autograd=====

# x1 = torch.ones(3, 3)
# print(x1)

x = torch.ones(2, 2, requires_grad=True)
print(x)

y = x + 2
print(y)

# # 这个和上面的有什么区别？
# y1 = x1 + 2
# print(y1)

# print(x.grad_fn)
# print(y.grad_fn)

z = y * y * 3
out = z.mean()
# out = z.max()
print(z, out)
print('out_grad==', out.grad)

# 尝试， 和加requires_grad有什么区别么？
# z1 = y1 * y1 * 3
# out = z1.mean()
# print(z1, out)

# a = torch.randn(2, 2)
# a = torch.ones(2, 2)
# a = ((a * 3)) / (a - 1)
# print(a)
# a = (a * 3)
# print(a)
# print(a.requires_grad)
# a.requires_grad_(True)
# a = a.requires_grad(True) # 这样是不可以的， 因为不能回调
# print(a.requires_grad)
# print(a*a)
# b = (a * a).sum() # 将张量矩阵中的元素相加得到一个数
# print(b)
# print(b.grad_fn)

# out.backward()
# z.backward() # 注意这里求导的时候需要z或是out为标量， 也可以不为标量但是需要修改
'''
例如
>>> a=Variable(torch.FloatTensor([1,2,3]),requires_grad=True)
>>> b=3*a
>>> autograd.grad(outputs=b,inputs=a,grad_outputs=torch.ones_like(a))
(tensor([ 3.,  3.,  3.]),)

'''
# print('x.grad===',x.grad)

# print(x.requires_grad)
# print((x**2).requires_grad)

# with torch.no_grad():
#     print((x**2).requires_grad)

# print(x.requires_grad)
# y = x.detach()
# print(y.requires_grad)
# torch.eq()代表相等
# tensor.all()功能: 如果张量tensor中所有元素都是True, 才返回True; 否则返回False
# tensor.any()功能: 如果张量tensor中存在一个元素为True, 那么返回True; 只有所有元素都是False时才返回False
# print(x.eq(y).all())





